# Copyright (c) 2007 Fabio Makoto Akita
# 
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
# 
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
# 
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

# Author: AkitaOnRails (www.akitaonrails.com)
# Support: Surgeworks LLC (www.surgeworks.com)

#
# WARNING: DO NOT EDIT THIS FILE MANUALLY - IT IS AUTOMATICALLY GENERATED BY THE PLUGIN
#
module SyncsHelper
  module Processes
    # timestamp for the sync logs
    def log_timestamp
      Time.now.iso8601
    end
    
    # formats a given interval Time - Time, in 00:00:00 format
    def log_fmt_timestamp(time)
      [(time/3600).to_i, (time/60 % 60).to_i, (time % 60).to_i].map{|t| t.to_s.rjust(2,'0')}.join(':')
    end
    
    # reset and starts log file
    def delete_log(log_file = '')
      File.delete(log_file) if File.exists?(log_file) && $SYNC_CONFIG[:wipe_log]
      log_file
    end

    # check if a process is running
    def pid_running?(pid = nil)
      return false unless pid
      
      processor, platform, *rest = RUBY_PLATFORM.split("-")
      running = false
      if platform == 'mswin32'
        require 'win32/process'
        require 'win32ole'
        processes = WIN32OLE.connect("winmgmts://").ExecQuery("select * from win32_process")
        for process in processes do
          running = true if process.ProcessId == pid
        end
      else
        out = IO.popen("ps ux | grep #{pid}")
        buffer = out.readlines.inject([]) { |b,l| b << l.split(' ') }
        buffer.each do |p|
          running = true if p[1] == pid
        end
      end
      running
    end
    
    def job_running?
      pid_running?(read_pid)
    end
    
    # caches an entire log file (to only be used in the jobs where the log files
    # are wiped off in every run)
    # sets an instance variable @last_line
    def read_log(log_file = '')
      buffer = ''
      if File.exist?(log_file)
        begin
          File.open(log_file, 'r') do |f| 
            while line = f.gets
              buffer << "#{line}\n"
              @last_line = line
            end
          end
        rescue => e
          logger.error "Something wrong reading log file #{log_file}, #{e.inspect}"
        end
      end
      buffer.strip
    end

    # start a singleton background job associated with a Rails environment
    def start_runner(task)
      runner = SyncsHelper::Runner.get(task)
      raise "Invalid Runner Task" unless runner
      runner_params, log_file = runner.to_a
      
      processor, platform, *rest = RUBY_PLATFORM.split("-")
      command = if platform != 'mswin32'
          "./script/runner #{runner_params}"
        else
          ruby_path = File.join(Dir.pwd, '..', 'ruby', 'bin', 'rubyw.exe')
          "#{ruby_path} script\\runner #{runner_params}"
        end

      delete_log log_file
      pid = run_process(command, false)
      write_pid(pid)
    end

    # wraps the creation of a single external process
    # the 'wait' parameter flags if the parent process should wait or detach
    def run_process(command, wait = true)
      processor, platform, *rest = RUBY_PLATFORM.split("-")
      if platform != 'mswin32'
        pid = Process.fork do
          exec command
          exit! 127
        end
        wait ? Process.waitpid(pid) : Process.detach(pid)
      else
        require 'win32/process'
        my_out = $stdout.clone
        my_err = $stderr.clone
        my_out.reopen('NUL')
        my_err.reopen('NUL')

        pid = Process.create(:app_name => command, :inherit => true, :cwd => Dir.pwd,
           :creation_flags => Process::DETACHED_PROCESS,
           :startup_info => {
              :startf_flags => Process::STARTF_USESTDHANDLES,
              :stdout => my_out,
              :stderr => my_err
           }).process_id
        Process.waitpid(pid) if wait
      end
      pid
    end
    
    def pid_file
      File.join(Dir.pwd, 'log', 'job.pid')
    end
    
    def write_pid(pid)
      File.open(pid_file, 'w') { |f| f.puts pid }
      pid
    end
    
    def read_pid
      read_log(pid_file)
    end
  end
end

module SyncsHelper
  class Runner
    @@runners = {}
    attr_accessor :params, :log_file
    
    def initialize(task = nil, log_file = nil)
      self.params   = runner_param("#{task}.rb")
      self.log_file = File.join(RAILS_ROOT, log_file) if log_file
      @@runners.merge!( task => self )
    end
   
    def to_a
      [self.params.call, self.log_file]
    end
    
    def self.get(runner = nil)
      @@runners[runner]
    end
    
    private
    
    def runner_param(file)
      lambda {
        script_file = File.join('lib', 'daemons', file)
        "'@logged_user=\"#{User.current_user.id}\"; load \"#{script_file}\"' -e #{ENV['RAILS_ENV']}"
      }
    end
  end

  ReplicatorRunner = Runner.new(:replicator, $SYNC_CONFIG[:log_file])
  UpgradeRunner    = Runner.new(:upgrade   , 'log/upgrade.log')
  FirstTimeRunner  = Runner.new(:first_time, 'log/first_time.log')
  RedbookRunner    = Runner.new(:redbook   , $SYNC_CONFIG[:redbook_log_file])
end